package org.toves.checklua.scan;
import java.util.HashMap;
import java.util.regex.Pattern;



public class LuaScanner extends TokenScanner {
	private static final HashMap<String,TokenType> SYMBOL_TOKENS;
	private static final HashMap<String,TokenType> WORD_TOKENS;
	
	static {
		SYMBOL_TOKENS = new HashMap<String,TokenType>();
		SYMBOL_TOKENS.put("(",  LuaToken.LPAREN);
		SYMBOL_TOKENS.put(")",  LuaToken.RPAREN);
		SYMBOL_TOKENS.put("[",  LuaToken.LBRACK);
		SYMBOL_TOKENS.put("]",  LuaToken.RBRACK);
		SYMBOL_TOKENS.put("{",  LuaToken.LBRACE);
		SYMBOL_TOKENS.put("}",  LuaToken.RBRACE);
		SYMBOL_TOKENS.put("..", LuaToken.APPEND);
		SYMBOL_TOKENS.put("=",  LuaToken.ASSIGN);
		SYMBOL_TOKENS.put("^",  LuaToken.CARET);
		SYMBOL_TOKENS.put(":",  LuaToken.COLON);
		SYMBOL_TOKENS.put(",",  LuaToken.COMMA);
		SYMBOL_TOKENS.put(".",  LuaToken.DOT);
		SYMBOL_TOKENS.put("...", LuaToken.ELLIPSIS);
		SYMBOL_TOKENS.put("==", LuaToken.EQ);
		SYMBOL_TOKENS.put(">=", LuaToken.GE);
		SYMBOL_TOKENS.put(">",  LuaToken.GT);
		SYMBOL_TOKENS.put("<=", LuaToken.LE);
		SYMBOL_TOKENS.put("<",  LuaToken.LT);
		SYMBOL_TOKENS.put("-",  LuaToken.MINUS);
		SYMBOL_TOKENS.put("~=", LuaToken.NE);
		SYMBOL_TOKENS.put("%",  LuaToken.PERCENT);
		SYMBOL_TOKENS.put("+",  LuaToken.PLUS);
		SYMBOL_TOKENS.put(";",  LuaToken.SEMI);
		SYMBOL_TOKENS.put("#",  LuaToken.SHARP);
		SYMBOL_TOKENS.put("/",  LuaToken.SLASH);
		SYMBOL_TOKENS.put("*",  LuaToken.STAR);

		WORD_TOKENS = new HashMap<String,TokenType>();
		WORD_TOKENS.put("and",    LuaToken.AND);
		WORD_TOKENS.put("break",  LuaToken.BREAK);
		WORD_TOKENS.put("do",     LuaToken.DO);
		WORD_TOKENS.put("else",   LuaToken.ELSE);
		WORD_TOKENS.put("elseif", LuaToken.ELSEIF);
		WORD_TOKENS.put("end",    LuaToken.END);
		WORD_TOKENS.put("false",  LuaToken.FALSE);
		WORD_TOKENS.put("for",    LuaToken.FOR);
		WORD_TOKENS.put("function", LuaToken.FUNCTION);
		WORD_TOKENS.put("if",     LuaToken.IF);
		WORD_TOKENS.put("in",     LuaToken.IN);
		WORD_TOKENS.put("local",  LuaToken.LOCAL);
		WORD_TOKENS.put("nil",    LuaToken.NIL);
		WORD_TOKENS.put("not",    LuaToken.NOT);
		WORD_TOKENS.put("or",     LuaToken.OR);
		WORD_TOKENS.put("repeat", LuaToken.REPEAT);
		WORD_TOKENS.put("return", LuaToken.RETURN);
		WORD_TOKENS.put("then",   LuaToken.THEN);
		WORD_TOKENS.put("true",   LuaToken.TRUE);
		WORD_TOKENS.put("until",  LuaToken.UNTIL);
		WORD_TOKENS.put("while",  LuaToken.WHILE);
	}
	
	private static class SymbolToken extends TokenType {
		public SymbolToken() { super("symbol"); }
		@Override
		public TokenType process(TokenScanner scanner, String text) {
			TokenType ret = SYMBOL_TOKENS.get(text);
			return ret == null ? TOKEN_ERROR : ret;
		}
	}
	
	private static class WordToken extends TokenType {
		public WordToken() { super("word"); }
		@Override
		public TokenType process(TokenScanner scanner, String text) {
			TokenType ret = WORD_TOKENS.get(text);
			return ret == null ? LuaToken.IDENT : ret;
		}
	}
	
	private static String hashKeysToRegex(HashMap<String,TokenType> hash) {
		StringBuilder ret = new StringBuilder();
		for (String key : hash.keySet()) {
			if (ret.length() > 0) {
				ret.append("|");
			}
			ret.append(Pattern.quote(key));
		}
		return ret.toString();
	}
	
	public LuaScanner() {
		addRegex("--[^\\n]*", null);
		addRegex(hashKeysToRegex(SYMBOL_TOKENS), new SymbolToken());
		addRegex("[a-zA-Z_][a-zA-Z_0-9]*", new WordToken());
		addRegex("\\s+", null);
		addRegex("'([^'\\\\\\n]|\\\\.)*'", LuaToken.STRING);
		addRegex("\"([^\"\\\\\\n]|\\\\.)*\"", LuaToken.STRING);
		addRegex("0x[0-9a-fA-F]+|[0-9]+(\\.[0-9]*)?([eE]-?[0-9]+)?", LuaToken.NUMBER);
		addRegex("'([^'\\\\\\n]|\\\\.)*", LuaToken.UNTERMINATED_STRING);
		addRegex("\"([^\"\\\\\\n]|\\\\.)*", LuaToken.UNTERMINATED_STRING);
	}
}
